001 /*
002 * Copyright 2004 Niclas Hedhman
003 * Copyright 2004-2006 Stephen J. McConnell
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License");
006 * you may not use this file except in compliance with the License.
007 * You may obtain a copy of the License at
008 *
009 * http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
014 * implied.
015 *
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 */
019
020 package net.dpml.transit.artifact;
021
022 import java.io.InputStream;
023 import java.io.IOException;
024 import java.io.OutputStream;
025 import java.io.File;
026 import java.net.UnknownServiceException;
027 import java.net.URI;
028 import java.net.URL;
029 import java.net.URLConnection;
030 import java.net.URISyntaxException;
031
032 import net.dpml.transit.Artifact;
033 import net.dpml.transit.Transit;
034 import net.dpml.transit.SecuredTransitContext;
035 import net.dpml.transit.CacheHandler;
036
037 import net.dpml.lang.Part;
038
039 import net.dpml.util.MimeTypeHandler;
040
041 /**
042 * The connection handler for URLs based on the "artifact" protocol family.
043 * @author <a href="http://www.dpml.net">The Digital Product Meta Library</a>
044 * @version 1.0.0
045 */
046 public class ArtifactURLConnection extends URLConnection
047 {
048 // ------------------------------------------------------------------------
049 // state
050 // ------------------------------------------------------------------------
051
052 /**
053 * Transit context.
054 */
055 private final SecuredTransitContext m_context;
056
057 /**
058 * Artifact.
059 */
060 private final Artifact m_artifact;
061
062 /**
063 * Reference fragment.
064 */
065 private final String m_reference;
066
067 /**
068 * The connected state.
069 */
070 private boolean m_connected;
071
072 // ------------------------------------------------------------------------
073 // constructor
074 // ------------------------------------------------------------------------
075
076 /**
077 * Creation of a new handler.
078 * @param url the url to establish a connection with
079 * @param context the transit context
080 * @exception NullPointerException if the url argument is null
081 * @exception IOException if the url argument is not a valid,
082 * i.e. the path must contain a group and an artifactId
083 * separated by a slash.
084 */
085 ArtifactURLConnection( URL url, SecuredTransitContext context )
086 throws NullPointerException, IOException
087 {
088 super( url );
089
090 Transit.getInstance(); // make sure Transit is initialized
091
092 m_context = context;
093 m_reference = getReference( url );
094
095 String spec = getRealSpec( url, m_reference );
096 try
097 {
098 m_artifact = Artifact.createArtifact( spec );
099 }
100 catch( URISyntaxException e )
101 {
102 throw new IOException( e.toString() );
103 }
104 }
105
106 // ------------------------------------------------------------------------
107 // URLConnection
108 // ------------------------------------------------------------------------
109
110 /**
111 * Establish a connection. The implementation will attempt to
112 * resolve the resource relative to the cache and associated hosts.
113 *
114 * @exception IOException is an error occurs while attempting to establish
115 * the connection.
116 */
117 public void connect()
118 throws IOException
119 {
120 m_connected = true;
121 }
122
123 /**
124 * Return an input stream to the resource.
125 * @return the input stream
126 * @exception IOException is an error occurs
127 */
128 public InputStream getInputStream()
129 throws IOException
130 {
131 connect();
132 CacheHandler cache = m_context.getCacheHandler();
133 if( null != m_reference )
134 {
135 return cache.getResource( m_artifact, m_reference );
136 }
137 else
138 {
139 return cache.getResource( m_artifact );
140 }
141 }
142
143 /**
144 * Return an output stream to the resource.
145 * @return the output stream
146 * @exception IOException if any I/O problems occur.
147 */
148 public OutputStream getOutputStream()
149 throws IOException
150 {
151 CacheHandler cache = m_context.getCacheHandler();
152 return cache.createOutputStream( m_artifact );
153 }
154
155 /**
156 * Reutrn the mimetype of the content.
157 * @return the content mimetype
158 */
159 public String getContentType()
160 {
161 String type = m_artifact.getType();
162 return MimeTypeHandler.getMimeType( type );
163 }
164
165 /**
166 * Return the content for this artifact.
167 * @return the content object (possibly null)
168 * @exception IOException is an error occurs
169 */
170 public Object getContent()
171 throws IOException
172 {
173 Object content = getContent( new Class[0] );
174 if( content != null )
175 {
176 return content;
177 }
178 else
179 {
180 return super.getContent();
181 }
182 }
183
184 /**
185 * Return the content for this artifact.
186 * @param classes a sequence of classes against which the
187 * implementation will attempt to establish a known match
188 * @return the content object (possibly null)
189 * @exception IOException is an error occurs
190 */
191 public Object getContent( Class[] classes )
192 throws IOException
193 {
194 String type = m_artifact.getType();
195
196 //
197 // if the type is a plugin then handle this directly
198 //
199
200 if( "part".equals( type ) )
201 {
202 URI uri = m_artifact.toURI();
203 Part part = Part.load( uri );
204 return part.getContent( classes );
205 }
206
207 //
208 // otherwise fallback on the default jvm content handling
209 //
210
211 try
212 {
213 Object content = super.getContent( classes );
214 if( content != null )
215 {
216 return content;
217 }
218 }
219 catch( UnknownServiceException use )
220 {
221 boolean ignoreThis = true;
222 }
223
224 //
225 // attempt to resolve this locally as we may be dealing
226 // with Magic references to the artifact File
227 //
228
229 for( int i=0; i < classes.length; i++ )
230 {
231 Class c = classes[i];
232 if( c.equals( File.class ) )
233 {
234 return m_context.getCacheHandler().getLocalFile( m_artifact );
235 }
236 }
237 return null;
238 }
239
240 // ------------------------------------------------------------------------
241 // implementation
242 // ------------------------------------------------------------------------
243
244 /**
245 * Return a fragment referencing content within the resource referenced by
246 * the artifact.
247 * @param url the url
248 * @return the fragment or null if this is not a referential url
249 */
250 private String getReference( URL url )
251 {
252 String path = url.getPath();
253 int i = path.indexOf( '!' );
254 if( i < 0 )
255 {
256 return null;
257 }
258 else
259 {
260 return path.substring( i );
261 }
262 }
263
264 /**
265 * Return the real specification of the supplied url.
266 * @param url the url to evaluate
267 * @param ref a reference fragment
268 * @return the artifact url spec withough the ref fragment
269 */
270 private String getRealSpec( URL url, String ref )
271 {
272 if( null != ref )
273 {
274 String spec = url.toString();
275 int j = spec.indexOf( ref );
276 if( j > 0 )
277 {
278 String s = spec.substring( 0, j );
279 String version = url.getUserInfo();
280 if( null != version )
281 {
282 s = s + "#" + version;
283 }
284 return s;
285 }
286 }
287 return url.toString();
288 }
289 }